Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* scram.c
3 : * strophe XMPP client library
4 : *
5 : * SCRAM-SHA1 helper functions according to RFC5802
6 : * HMAC-SHA1 implementation according to RFC2104
7 : *
8 : * Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
9 : *
10 : * This software is provided AS-IS with no warranty, either express
11 : * or implied.
12 : *
13 : * This program is dual licensed under the MIT or GPLv3 licenses.
14 : */
15 :
16 : /** @file
17 : * SCRAM-SHA1 helper functions.
18 : */
19 :
20 : #include <assert.h>
21 : #include <string.h>
22 :
23 : #include "common.h"
24 : #include "sha1.h"
25 : #include "sha256.h"
26 : #include "sha512.h"
27 : #include "ostypes.h"
28 :
29 : #include "scram.h"
30 :
31 : #define HMAC_BLOCK_SIZE_MAX 128
32 :
33 : static const uint8_t ipad = 0x36;
34 : static const uint8_t opad = 0x5C;
35 :
36 : const struct hash_alg scram_sha1 = {
37 : "SCRAM-SHA-1",
38 : SASL_MASK_SCRAMSHA1,
39 : SHA1_DIGEST_SIZE,
40 : (void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
41 : (void (*)(void *))crypto_SHA1_Init,
42 : (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
43 : (void (*)(void *, uint8_t *))crypto_SHA1_Final};
44 :
45 : const struct hash_alg scram_sha1_plus = {
46 : "SCRAM-SHA-1-PLUS",
47 : SASL_MASK_SCRAMSHA1_PLUS,
48 : SHA1_DIGEST_SIZE,
49 : (void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
50 : (void (*)(void *))crypto_SHA1_Init,
51 : (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
52 : (void (*)(void *, uint8_t *))crypto_SHA1_Final};
53 :
54 : const struct hash_alg scram_sha256 = {
55 : "SCRAM-SHA-256",
56 : SASL_MASK_SCRAMSHA256,
57 : SHA256_DIGEST_SIZE,
58 : (void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
59 : (void (*)(void *))sha256_init,
60 : (void (*)(void *, const uint8_t *, size_t))sha256_process,
61 : (void (*)(void *, uint8_t *))sha256_done};
62 :
63 : const struct hash_alg scram_sha256_plus = {
64 : "SCRAM-SHA-256-PLUS",
65 : SASL_MASK_SCRAMSHA256_PLUS,
66 : SHA256_DIGEST_SIZE,
67 : (void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
68 : (void (*)(void *))sha256_init,
69 : (void (*)(void *, const uint8_t *, size_t))sha256_process,
70 : (void (*)(void *, uint8_t *))sha256_done};
71 :
72 : const struct hash_alg scram_sha512 = {
73 : "SCRAM-SHA-512",
74 : SASL_MASK_SCRAMSHA512,
75 : SHA512_DIGEST_SIZE,
76 : (void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
77 : (void (*)(void *))sha512_init,
78 : (void (*)(void *, const uint8_t *, size_t))sha512_process,
79 : (void (*)(void *, uint8_t *))sha512_done};
80 :
81 : const struct hash_alg scram_sha512_plus = {
82 : "SCRAM-SHA-512-PLUS",
83 : SASL_MASK_SCRAMSHA512_PLUS,
84 : SHA512_DIGEST_SIZE,
85 : (void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
86 : (void (*)(void *))sha512_init,
87 : (void (*)(void *, const uint8_t *, size_t))sha512_process,
88 : (void (*)(void *, uint8_t *))sha512_done};
89 :
90 : /* The order of this list defines the order in which the SCRAM algorithms are
91 : * tried if the server supports them.
92 : * Their order is derived from
93 : * https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage
94 : */
95 : const struct hash_alg *scram_algs[] = {
96 : /* *1 */
97 : &scram_sha512_plus,
98 : /* *1 */
99 : &scram_sha256_plus,
100 : /* *1 */
101 : &scram_sha1_plus,
102 : /* *1 */
103 : &scram_sha512,
104 : /* *1 */
105 : &scram_sha256,
106 : /* *1 */
107 : &scram_sha1,
108 : };
109 : /* *1 - I want to use clang-format here, but by default it will put multiple
110 : * elements per line if there's no comment. Currently clang-format also doesn't
111 : * have an option to enforce it to behave like that, besides by putting comments
112 : * between each element (or I couldn't find a way to do it). In order to prevent
113 : * clang-format from re-formatting the array I added the comments above and
114 : * wrote this lengthy description.
115 : */
116 : const size_t scram_algs_num = sizeof(scram_algs) / sizeof(scram_algs[0]);
117 :
118 : union common_hash_ctx {
119 : SHA1_CTX sha1;
120 : sha256_context sha256;
121 : sha512_context sha512;
122 : };
123 :
124 0 : static void crypto_HMAC(const struct hash_alg *alg,
125 : const uint8_t *key,
126 : size_t key_len,
127 : const uint8_t *text,
128 : size_t len,
129 : uint8_t *digest)
130 : {
131 0 : uint8_t key_pad[HMAC_BLOCK_SIZE_MAX];
132 0 : uint8_t key_ipad[HMAC_BLOCK_SIZE_MAX];
133 0 : uint8_t key_opad[HMAC_BLOCK_SIZE_MAX];
134 0 : uint8_t sha_digest[SCRAM_DIGEST_SIZE];
135 0 : size_t blocksize;
136 0 : size_t i;
137 0 : union common_hash_ctx ctx;
138 :
139 0 : assert(alg->digest_size <= HMAC_BLOCK_SIZE_MAX);
140 0 : blocksize = alg->digest_size < 48 ? 64 : 128;
141 :
142 0 : memset(key_pad, 0, blocksize);
143 0 : if (key_len <= blocksize) {
144 0 : memcpy(key_pad, key, key_len);
145 : } else {
146 : /* according to RFC2104 */
147 0 : alg->hash(key, key_len, key_pad);
148 : }
149 :
150 0 : for (i = 0; i < blocksize; i++) {
151 0 : key_ipad[i] = key_pad[i] ^ ipad;
152 0 : key_opad[i] = key_pad[i] ^ opad;
153 : }
154 :
155 0 : alg->init((void *)&ctx);
156 0 : alg->update((void *)&ctx, key_ipad, blocksize);
157 0 : alg->update((void *)&ctx, text, len);
158 0 : alg->final((void *)&ctx, sha_digest);
159 :
160 0 : alg->init((void *)&ctx);
161 0 : alg->update((void *)&ctx, key_opad, blocksize);
162 0 : alg->update((void *)&ctx, sha_digest, alg->digest_size);
163 0 : alg->final((void *)&ctx, digest);
164 0 : }
165 :
166 0 : static void SCRAM_Hi(const struct hash_alg *alg,
167 : const uint8_t *text,
168 : size_t len,
169 : const uint8_t *salt,
170 : size_t salt_len,
171 : uint32_t i,
172 : uint8_t *digest)
173 : {
174 0 : size_t k;
175 0 : uint32_t j;
176 0 : uint8_t tmp[128];
177 :
178 0 : static uint8_t int1[] = {0x0, 0x0, 0x0, 0x1};
179 :
180 : /* assume salt + INT(1) isn't longer than sizeof(tmp) */
181 0 : assert(salt_len <= sizeof(tmp) - sizeof(int1));
182 :
183 0 : memset(digest, 0, alg->digest_size);
184 0 : if (i == 0) {
185 0 : return;
186 : }
187 :
188 0 : memcpy(tmp, salt, salt_len);
189 0 : memcpy(&tmp[salt_len], int1, sizeof(int1));
190 :
191 : /* 'text' for Hi is a 'key' for HMAC */
192 0 : crypto_HMAC(alg, text, len, tmp, salt_len + sizeof(int1), digest);
193 0 : memcpy(tmp, digest, alg->digest_size);
194 :
195 0 : for (j = 1; j < i; j++) {
196 0 : crypto_HMAC(alg, text, len, tmp, alg->digest_size, tmp);
197 0 : for (k = 0; k < alg->digest_size; k++) {
198 0 : digest[k] ^= tmp[k];
199 : }
200 : }
201 : }
202 :
203 0 : void SCRAM_ClientKey(const struct hash_alg *alg,
204 : const uint8_t *password,
205 : size_t len,
206 : const uint8_t *salt,
207 : size_t salt_len,
208 : uint32_t i,
209 : uint8_t *key)
210 : {
211 0 : uint8_t salted[SCRAM_DIGEST_SIZE];
212 :
213 : /* XXX: Normalize(password) is omitted */
214 :
215 0 : SCRAM_Hi(alg, password, len, salt, salt_len, i, salted);
216 0 : crypto_HMAC(alg, salted, alg->digest_size, (uint8_t *)"Client Key",
217 : strlen("Client Key"), key);
218 0 : }
219 :
220 0 : void SCRAM_ClientSignature(const struct hash_alg *alg,
221 : const uint8_t *ClientKey,
222 : const uint8_t *AuthMessage,
223 : size_t len,
224 : uint8_t *sign)
225 : {
226 0 : uint8_t stored[SCRAM_DIGEST_SIZE];
227 :
228 0 : alg->hash(ClientKey, alg->digest_size, stored);
229 0 : crypto_HMAC(alg, stored, alg->digest_size, AuthMessage, len, sign);
230 0 : }
231 :
232 0 : void SCRAM_ClientProof(const struct hash_alg *alg,
233 : const uint8_t *ClientKey,
234 : const uint8_t *ClientSignature,
235 : uint8_t *proof)
236 : {
237 0 : size_t i;
238 0 : for (i = 0; i < alg->digest_size; i++) {
239 0 : proof[i] = ClientKey[i] ^ ClientSignature[i];
240 : }
241 0 : }
|